package com.cskaoyan.javase.oop3._2inner._6lambda._4example;

/**
 * Lambda表达式具体使用案例举例
 *      Lambda表达式传给一个方法,本质上就是将一个方法传给一个方法
 *      这个传入方法的回调方法用于表示某一种规则
 *          比如:
 *          计算规则
 *          过滤规则filter
 *          映射规则map
 *          规约规则reduce
 *          排序sort
 *          ...
 * @since 11:30
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // 过滤一个String数组中的"abc"元素
        String[] old = {"abc", "abc", "123", "1"};

        Object[] result = filterObjectArr(old, o -> {
            String str = (String) o;
            return !str.equals("abc");
        });
        // 数组类型是不能强转的
        for (Object o : result) {
            String str = (String) o;
            System.out.println(str);
        }
        System.out.println("--------------------");
        Student[] old2 = new Student[5];
        old2[0] = new Student(18, 100);
        old2[1] = new Student(28, 80);
        old2[2] = new Student(8, 59);
        old2[3] = new Student(7, 90);
        old2[4] = new Student(17, 70);
        // 过滤未成年学生
        Object[] result2 = filterObjectArr(old2, o -> {
            Student s = (Student) o;
            return s.age >= 18;
        });
        for (Object o : result2) {
            Student s = (Student) o;
            s.print();
        }

        System.out.println("-----------------");
        // 过滤及格的学生
        Object[] result3 = filterObjectArr(old2, o -> {
            Student s = (Student) o;
            return s.score < 60;
        });
        for (Object o : result3) {
            Student s = (Student) o;
            s.print();
        }
        /*
            如何过滤基本数据类型元素的数组？
                包装类型
                就是将一个基本数据类型变量封装成一个对象
                集合中只能存放对象
         */
        int[] arr = {1, 2, 3};
        Integer[] arr2 = {1, 2, 3};

        /*
            使用Object[]数组作为参数有点不方便
            需要频繁的进行强制
            为了弥补这个缺陷
            Java推出了泛型的技术
            可以让编译器记住对应的类型，同时兼顾Object的灵活性，也不会需要强转
         */

    }

    /*
        利用Lambda表达式和回调实现对任意数组的任意过滤规则
        过滤的方法怎么写呢?
        对数组元素进行过滤?将数组过滤剩下的元素,封装成一个新数组
        所以方法需要返回这个新数组
        并且方法的形参,也需要一个被过滤的数组'
        还需要一个过滤规则
        于是方法的声明就写完了
     */
    public static Object[] filterObjectArr(Object[] old, IFilter filter) {
        // 统计新数组的长度,就是过滤留下元素的个数
        int count = 0;
        for (Object o : old) {
            if (filter.accept(o)) {
                // 留下的元素
                count++;
            }
        }
        // count就是新数组的长度
        Object[] result = new Object[count];
        int newIndex = 0;
        // 给新数组赋值
        for (Object o : old) {
            if (filter.accept(o)) {
                result[newIndex] = o;
                newIndex++;
            }
        }
        return result;
    }

}

/*
    既然是过滤,就需要一个过滤的规则,这个规则用功能接口中的抽象方法来表示
    具体实现这个规则用Lambda表达式
  */
interface IFilter {
    /*
        过滤的规则咋做?
        这里需要过滤所有类型数组中的元素
        过滤元素时,要么过滤成功,要么过滤失败,这种二元状态,可以用布尔类型表示
        这里用true表示过滤成功,表示留下的元素
            false表示过滤失败,表示被丢弃的元素
        既然是过滤元素,肯定需要对这个元素做判断,所以方法需要一个参数
        而且由于我们希望能够过滤所有类型的数组,形参类型应该写Object
     */
    boolean accept(Object o);
}

class Student {
    int age;
    double score;

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    public void print() {
        System.out.println("age=" + age + ",score = " + score);
    }
}